#include <SDL.h>
#include <SDL_image.h>
#include <SDL_rotozoom.h>
#include <SDL_mixer.h>
#include <math.h>
#include <vector>

#include "Enemy.h"
#include "sdlfunc.h"
#include "Werewolf.h"
#include "Ghost.h"
#include "Zombie.h"
#include "mt19937ar.h"
#include "picgen.h"

SDL_Surface* screen;
Uint8* keystate;

//WARNING: THIS CODE IS NOT FOR THE FAINT OF HEART
int main(int argc, char* argv[])
{
// <INITIALIZATION>------------------------------------------------------
    InitializeSDL();
    init_genrand(0);
    Picgen myPicgen;
    //use this to skip to certain scenes to debug
    goto title;
// </INITIALIZATION>------------------------------------------------------

// <TITLE SCREEN>----------------------------------------------------
    title:
        {
            //load resources
            SDL_Surface* titlescreen = SDL_LoadBMP("titlescreen.bmp");
            //TODO: load music

            SDL_BlitSurface(titlescreen,NULL,screen,NULL);
            SDL_Flip(screen);
            while(true)
            {
                SDL_PumpEvents();
                if(keystate[SDLK_RETURN])
                    break;
                if(keystate[SDLK_ESCAPE])
                    break;
            }
            SDL_FreeSurface(titlescreen);
            //TODO: unload music
            if(keystate[SDLK_ESCAPE])
                goto quit;
            //fade out
            SDL_Surface* blackness;
            blackness = SDL_CreateRGBSurface(screen->flags,screen->w,screen->h,screen->format->BitsPerPixel,screen->format->Rmask,screen->format->Gmask,screen->format->Bmask,screen->format->Amask);
            SDL_FillRect(screen,NULL,SDL_MapRGBA(screen->format,0,0,0,SDL_ALPHA_TRANSPARENT-1));
            for(unsigned int i = 0; i < SDL_ALPHA_TRANSPARENT; i++)
            {
                SDL_BlitSurface(blackness,NULL,screen,NULL);
                SDL_Delay(10);
                SDL_Flip(screen);
            }
            SDL_FreeSurface(blackness);
        }
        goto game;
// </TITLE SCREEN>----------------------------------------------------

// <GAME SCREEN>------------------------------------------------------
    game:
        {
            // constants
            const int SCROLL_SPEED = 5; //px/s
            const Uint32 LevelLength = 30000;
            const int HUD_HEIGHT = 100;
            int spawndelay = 3000;
            const int enemytypes = 3;

            //surfaces
            SDL_Surface* grass = SDL_LoadBMP("grass.bmp");
            SDL_Surface* grasstodirt = SDL_LoadBMP("grasstodirt.bmp");
            SDL_Surface* dirt = SDL_LoadBMP("dirt.bmp");
            SDL_Surface* dirttograss = SDL_LoadBMP("dirttograss.bmp");
            SDL_Surface* PlayerFrame1 = IMG_Load("playerframe1.png");
            SDL_Surface* PlayerFrame2 = IMG_Load("playerframe2.png");
            SDL_Surface* BaseFlashLight = IMG_Load("flashlight.png");
            Werewolf::sprite1 = IMG_Load("Werewolf1.png");
            Werewolf::sprite2 = IMG_Load("Werewolf2.png");
            Ghost::sprite1 = IMG_Load("Ghost1.png");
            Ghost::sprite2 = IMG_Load("Ghost2.png");
            Zombie::sprite1 = IMG_Load("Zombie1.png");
            Zombie::sprite2 = IMG_Load("Zombie2.png");
            SDL_Surface* hudbg = SDL_LoadBMP("HUD.bmp");
            SDL_Surface* hudbg2 = SDL_LoadBMP("HUD2.bmp");
            SDL_Surface* CameraFlash = IMG_Load("cameraflash.png");
            SDL_Surface* death1 = IMG_Load("chardeath1.png");
            SDL_Surface* death2 = IMG_Load("chardeath2.png");
            SDL_Surface* death3 = IMG_Load("chardeath3.png");
            Werewolf::mugshot = IMG_Load("werewolfnobg.png");
            Ghost::mugshot = IMG_Load("ghostnobg.png");
            Zombie::mugshot = IMG_Load("zombienobg.png");
            SDL_Surface* picbg = SDL_LoadBMP("picbg.bmp");
            myPicgen.New();

            // time attributes
            Uint32 lasttime = SDL_GetTicks();
            Uint32 time = SDL_GetTicks();
            Uint32 elapsedtime = 0;
            float progressbar=0;;

            //bg attributes
            SDL_Rect bgcoord;
            SDL_Rect bgcoordcopy;
            const int BG_Y_ORIGIN = -64;
            bgcoord.x = 0;
            bgcoord.y = BG_Y_ORIGIN;

            //player attributes
            Uint32 playertimer=0;
            SDL_Surface* currentAnimation = PlayerFrame1;
            SDL_Rect playercoord;
            playercoord.x = screen->w/2 - PlayerFrame1->w/2;
            playercoord.y = screen->h - HUD_HEIGHT - PlayerFrame1->h;

            bool lighttoggle=true;
            bool lightlock=true;
            bool isalive = true;
            SDL_Rect lightcoord;
            double lightangle;

            int camerafadetime = 1000;
            int cameratime = 0;
            bool cameratoggle = false;
            SDL_Rect camcoord;
            int cameradelay = 0;

            bool quitflag = false;
            bool winflag = false;
            bool loseflag = false;

            //enemy attributes
            std::vector<Enemy*> enemies;
            int score =0;
            SDL_Surface* latestpicture = NULL;
            SDL_Surface* bestpicture = NULL;
            int enemytimer = 0;

            SDL_Rect spawns[12];
            spawns[0].x = -64; spawns[0].y = screen->h-HUD_HEIGHT-128;
            spawns[1].x = -64; spawns[1].y = screen->h-HUD_HEIGHT-256;
            spawns[2].x = -64; spawns[2].y = screen->h-HUD_HEIGHT-384;
            spawns[3].x = -64; spawns[3].y = screen->h-HUD_HEIGHT-512;
            spawns[4].x = 0; spawns[4].y = -64;
            spawns[5].x = 160; spawns[5].y = -64;
            spawns[6].x = 320; spawns[6].y = -64;
            spawns[7].x = 480; spawns[6].y = -64;
            spawns[8].x = 0; spawns[8].y = screen->h-HUD_HEIGHT-512;
            spawns[9].x = 0; spawns[9].y = screen->h-HUD_HEIGHT-384;
            spawns[10].x = 0; spawns[10].y = screen->h-HUD_HEIGHT-256;
            spawns[11].x = 0; spawns[11].y = screen->h-HUD_HEIGHT-128;

            //mouse attributes
            int mousex;
            int mousey;

            while(true)
            {
                //update controls
                SDL_PumpEvents();
                SDL_GetMouseState(&mousex,&mousey);

                if(keystate[SDLK_p])
                {
                    while(keystate[SDLK_p] != false){ SDL_PumpEvents(); }
                    while(true)
                    {
                        SDL_PumpEvents();
                        if(keystate[SDLK_p]){
                        while(keystate[SDLK_p] != false){ SDL_PumpEvents(); }
                            break;
                        }
                    }
                }
                time = SDL_GetTicks();
                //handle input
                if(keystate[SDLK_ESCAPE]){
                    quitflag = true;
                    break;
                }
                if(!keystate[SDLK_z]){
                    lightlock = false;
                }
                if(keystate[SDLK_z] && lightlock == false){
                    if(lighttoggle)
                    lighttoggle = false;
                    else lighttoggle = true;
                    lightlock = true;
                }

                progressbar += (float)elapsedtime /LevelLength;
                if(progressbar >= 100)
                {
                    //TODO: zip off screen
                    winflag = true;
                    break;
                }
                if(isalive == false)
                {
                    //TODO: play death sfx
                    SDL_FillRect(screen,NULL,SDL_MapRGB(screen->format,0,0,0));
                    SDL_BlitSurface(death1,NULL,screen,&playercoord);
                    SDL_Flip(screen);
                    SDL_Delay(1000);
                    SDL_FillRect(screen,NULL,SDL_MapRGB(screen->format,0,0,0));
                    SDL_BlitSurface(death2,NULL,screen,&playercoord);
                    SDL_Flip(screen);
                    SDL_Delay(1000);
                    SDL_FillRect(screen,NULL,SDL_MapRGB(screen->format,0,0,0));
                    SDL_BlitSurface(death3,NULL,screen,&playercoord);
                    SDL_Flip(screen);
                    SDL_Delay(3000);
                    loseflag = true;
                    break;
                }

                //calculate light angle
                if((mousex - (playercoord.x + PlayerFrame1->w/2)) == 0)
                    lightangle = M_PI_2;
                else{
                    if(mousex>= playercoord.x + PlayerFrame1->w/2)
                        lightangle = atan(((float)playercoord.y+(float)PlayerFrame1->h/2 - mousey)/((float)mousex - (playercoord.x + (float)PlayerFrame1->w/2)));
                    else if(mousex < playercoord.x + PlayerFrame1->w/2)
                        lightangle = -atan(((float)playercoord.y+(float)PlayerFrame1->h/2 - mousey)/((float)mousex - (playercoord.x + (float)PlayerFrame1->w/2)));
                }
                if(lightangle<0)
                    lightangle =0;
                else if(lightangle> M_PI)
                    lightangle = M_PI;
                lightangle = 180 * lightangle / M_PI; //convert to degrees

                //clear screen
                SDL_FillRect(screen,NULL,SDL_MapRGB(screen->format,0,0,0));

                //draw background
                //calculate offset
                bgcoord.y += (float)SCROLL_SPEED * elapsedtime/1000;
                if(bgcoord.y >= 0)
                    bgcoord.y = BG_Y_ORIGIN;
                for(int y =0; y < screen->h/grass->h; y++)
                {
                    for(int x =0; x < screen->w/grass->w; x++)
                    {
                        bgcoordcopy.y = bgcoord.y;
                        bgcoordcopy.x = 0;

                        bgcoordcopy.y += y* grass->h;
                        bgcoordcopy.x += x* grass->w;
                        if(x >=0 && x < 3)
                            SDL_BlitSurface(grass,NULL,screen,&bgcoordcopy);
                        else if(x==3)
                            SDL_BlitSurface(grasstodirt,NULL,screen,&bgcoordcopy);
                        else if(x>3 && x < 6)
                            SDL_BlitSurface(dirt,NULL,screen,&bgcoordcopy);
                        else if(x==6)
                            SDL_BlitSurface(dirttograss,NULL,screen,&bgcoordcopy);
                        else if(x>6)
                            SDL_BlitSurface(grass,NULL,screen,&bgcoordcopy);
                    }
                }

                //draw/create enemies
                enemytimer+=elapsedtime;
                if(enemytimer>=spawndelay)
                {
                    SDL_Rect spawnpoint;
                    int pointselect = floor((genrand_real1()*12-0.05));
                    if(pointselect<0)pointselect=0;
                    spawnpoint = spawns[pointselect];

                    int newtype = floor((genrand_real1()*enemytypes-0.05));
                    if(newtype<0)newtype =0;
                    switch(newtype)
                    {
                        case 0:
                            enemies.push_back(new Werewolf(spawnpoint.x,spawnpoint.y));
                            break;
                        case 1:
                            enemies.push_back(new Ghost(spawnpoint.x,spawnpoint.y));
                            break;
                        case 2:
                            enemies.push_back(new Zombie(spawnpoint.x,spawnpoint.y));
                            break;
                        default:
                            break;
                    } pointselect = floor((genrand_real1()*12-0.05));
                    if(pointselect<0)pointselect=0;
                    spawnpoint = spawns[pointselect];

                    newtype = floor((genrand_real2()*enemytypes-0.05));
                    if(newtype<0)newtype =0;
                    switch(newtype)
                    {
                        case 0:
                            enemies.push_back(new Werewolf(spawnpoint.x,spawnpoint.y));
                            break;
                        case 1:
                            enemies.push_back(new Ghost(spawnpoint.x,spawnpoint.y));
                            break;
                        case 2:
                            enemies.push_back(new Zombie(spawnpoint.x,spawnpoint.y));
                            break;
                        default:
                            break;
                    }
                    enemytimer =0;
                    spawndelay -= 50;
                }
                for(unsigned int i = 0; i < enemies.size(); i++)
                {
                    enemies[i]->Update(elapsedtime,lightangle,lighttoggle,mousex,mousey);
                    if(enemies[i]->y +enemies[i]->height > screen->h - HUD_HEIGHT-PlayerFrame1->h/2 && enemies[i]->x +enemies[i]->width > playercoord.x-20 && enemies[i]->x -20 < playercoord.x+PlayerFrame1->w)
                    {
                        isalive = false;
                    }
                }

                //draw flashlight
                if(lighttoggle){
                    SDL_Surface* newflashlight=NULL;
                    SDL_Surface* newnewflashlight=NULL;
                    if(mousex>=playercoord.x + PlayerFrame1->w/2){
                        newflashlight = rotozoomSurface(BaseFlashLight,lightangle,1,0);
                        lightcoord.x = screen->w/2 - newflashlight->w/2;
                        lightcoord.y = playercoord.y + PlayerFrame1->h/2 - newflashlight->h/2;
                        SDL_BlitSurface(newflashlight,NULL,screen,&lightcoord);
                     }else{
                        newflashlight = rotozoomSurface(BaseFlashLight,lightangle,1,0);
                        newnewflashlight = rotozoomSurfaceXY(newflashlight,0,-1,1,0);
                        lightcoord.x = screen->w/2 - newnewflashlight->w/2;
                        lightcoord.y = playercoord.y + PlayerFrame1->h/2 - newnewflashlight->h/2;
                        SDL_BlitSurface(newnewflashlight,NULL,screen,&lightcoord);
                    }
                    if(newflashlight!=NULL)SDL_FreeSurface(newflashlight);
                    if(newnewflashlight!=NULL)SDL_FreeSurface(newnewflashlight);
                    newflashlight=0;
                    newnewflashlight=0;
                }

                //draw/update camera
                cameradelay -= elapsedtime;
                if(cameratoggle){
                    cameratime+= elapsedtime;
                    if(cameratime>=camerafadetime)
                    {
                        cameratime = 0;
                        cameratoggle = false;
                    }
                }
                else if(SDL_GetMouseState(NULL, NULL)&SDL_BUTTON(SDL_BUTTON_LEFT) && cameradelay < 0)
                {
                    int distance;
                    float angle;
                    int degreerange = 30;
                    myPicgen.New();
                    //take picture here
                    for(unsigned int i = 0; i < enemies.size(); i++)
                    {
                        distance = sqrt((enemies[i]->x+enemies[i]->width/2 - screen->w/2)*(enemies[i]->x+enemies[i]->width/2 - screen->w/2) + ((screen->h-100) - (enemies[i]->y+enemies[i]->height/2))*((screen->h-100) - (enemies[i]->y+enemies[i]->height/2)));
                        if(abs(enemies[i]->x+enemies[i]->width/2 - screen->w/2) < 3) angle = M_PI_2;
                        else if(mousex > screen->w/2)
                            angle = atan(((screen->h - 100) - (enemies[i]->y + enemies[i]->height/2))/(enemies[i]->x+enemies[i]->width/2 - screen->w/2));
                        else if(mousex < screen->w/2)
                            angle = atan(((screen->h - 100) - (enemies[i]->y + enemies[i]->height/2))/-(enemies[i]->x+enemies[i]->width/2 - screen->w/2));
                        if(angle>M_PI)
                            angle = M_PI;
                        else if(angle < 0)
                            angle = 0;
                        angle = angle/M_PI * 180;

                        if(enemies[i]->x+ enemies[i]->width/2 <= screen->w/2 && mousex <= screen->w/2)
                        {
                            if(angle-degreerange <= lightangle && lightangle <= angle+degreerange){
                                myPicgen.Insert(enemies[i],distance);
                            }
                        }
                        else if(enemies[i]->x+enemies[i]->width/2 > screen->w/2 && mousex > screen->w/2)
                        {
                            if(angle-degreerange <= lightangle && lightangle <= angle+degreerange){
                                myPicgen.Insert(enemies[i],distance);
                            }
                        }

                    }
                    myPicgen.Points();
                    score += myPicgen.newscore;
                    int a = myPicgen.size;
                    cameratoggle = true;
                    cameradelay = 3000;
                }

               if(cameratoggle){
                    SDL_Surface* newcamflash=NULL;
                    SDL_Surface* newnewcamflash=NULL;
                    if(mousex>=playercoord.x + PlayerFrame1->w/2){
                        newcamflash = rotozoomSurface(CameraFlash,lightangle,1,0);
                        camcoord.x = screen->w/2 - newcamflash->w/2;
                        camcoord.y = playercoord.y + PlayerFrame1->h/2 - newcamflash->h/2;
                        SDL_BlitSurface(newcamflash,NULL,screen,&camcoord);
                     }else{
                        newcamflash = rotozoomSurface(CameraFlash,lightangle,1,0);
                        newnewcamflash = rotozoomSurfaceXY(newcamflash,0,-1,1,0);
                        camcoord.x = screen->w/2 - newnewcamflash->w/2;
                        camcoord.y = playercoord.y + PlayerFrame1->h/2 - newnewcamflash->h/2;
                        SDL_BlitSurface(newnewcamflash,NULL,screen,&camcoord);
                    }
                    if(newcamflash!=NULL)SDL_FreeSurface(newcamflash);
                    if(newnewcamflash!=NULL)SDL_FreeSurface(newnewcamflash);
                    newcamflash=0;
                    newnewcamflash=0;
                }

                //draw player
                playertimer += elapsedtime;
                if(playertimer > 500){
                    if(currentAnimation == PlayerFrame1)
                    currentAnimation = PlayerFrame2;
                    else currentAnimation = PlayerFrame1;
                    progressbar+= (float)100*playertimer/LevelLength;
                    playertimer = 0;
                }
                SDL_BlitSurface(currentAnimation,NULL,screen,&playercoord);

                //draw hud
                SDL_Rect hudcoords;
                hudcoords.x = 0; hudcoords.y = screen->h - HUD_HEIGHT;
                if(cameratoggle)
                    SDL_BlitSurface(hudbg2,NULL,screen,&hudcoords);
                else
                    SDL_BlitSurface(hudbg,NULL,screen,&hudcoords);
{
                    SDL_Rect pos;
                    pos.x = 474; pos.y = screen->h-HUD_HEIGHT+27;
                SDL_BlitSurface(picbg,NULL,screen,&pos);}
                if(myPicgen.size>0)
                {
                    SDL_Rect pos;
                    pos.x = 474; pos.y = screen->h-HUD_HEIGHT+27;
                    SDL_Rect photopos;
                    photopos.x = 473; photopos.y = screen->h - HUD_HEIGHT + 27;
                    SDL_Rect temppos;
                    int spacing = 150/myPicgen.size;
                    for(unsigned int i = 0; i < myPicgen.queue.size(); i++)
                    {
                        temppos = pos;
                        temppos.x += i*spacing;
                        switch(myPicgen.queue[i])
                        {
                            case WEREWOLF:
                            SDL_BlitSurface(Werewolf::mugshot,NULL,screen,&temppos);
                            break;
                            case GHOST:
                            SDL_BlitSurface(Ghost::mugshot,NULL,screen,&temppos);
                            break;
                            case ZOMBIE:
                            SDL_BlitSurface(Zombie::mugshot,NULL,screen,&temppos);
                            break;
                            default:
                            break;
                        }
                    }
                }
                //finally refresh the damn screen
                SDL_Flip(screen);

                //clean up enemies
                for(unsigned int i = 0; i < enemies.size(); i++)
                {
                    if(enemies[i]->deleteflag == true)
                        enemies.erase(enemies.begin()+i);
                }

                //cap fps
                if(time-lasttime < 1000/30)
                    SDL_Delay(time-lasttime);
                elapsedtime = SDL_GetTicks()-time;
                lasttime = time;
            }
            SDL_FreeSurface(grass);
            SDL_FreeSurface(grasstodirt);
            SDL_FreeSurface(dirt);
            SDL_FreeSurface(dirttograss);
            SDL_FreeSurface(PlayerFrame1);
            SDL_FreeSurface(PlayerFrame2);
            SDL_FreeSurface(BaseFlashLight);
            SDL_FreeSurface(Werewolf::sprite1);
            SDL_FreeSurface(Werewolf::sprite2);
            SDL_FreeSurface(Werewolf::mugshot);
            SDL_FreeSurface(Ghost::sprite1);
            SDL_FreeSurface(Ghost::sprite2);
            SDL_FreeSurface(Ghost::mugshot);
            SDL_FreeSurface(Zombie::sprite1);
            SDL_FreeSurface(Zombie::sprite2);
            SDL_FreeSurface(Zombie::mugshot);
            SDL_FreeSurface(hudbg);
            SDL_FreeSurface(hudbg2);
            SDL_FreeSurface(CameraFlash);
            SDL_FreeSurface(death1);
            SDL_FreeSurface(death2);
            SDL_FreeSurface(death3);
            SDL_FreeSurface(picbg);

            enemies.clear();

            if(quitflag) goto quit;
            if(winflag) goto win;
            if(loseflag) goto lose;
        }
// </GAME SCREEN>-----------------------------------------------------

// <WIN SCREEN>------------------------------------------------------
    win:
        {
            SDL_Surface* victorybg = SDL_LoadBMP("victoryscreen.bmp");
            SDL_BlitSurface(victorybg,NULL,screen,NULL);
            SDL_Rect pos; pos.x = 350; pos.y = 120;
            SDL_BlitSurface(myPicgen.bestpicture,NULL,screen,&pos);
            SDL_Flip(screen);
            while(true)
            {
                SDL_PumpEvents();
                if(keystate[SDLK_r] || keystate[SDLK_ESCAPE])
                    break;
            }
            SDL_FreeSurface(victorybg);
            if(keystate[SDLK_r])
                goto title;
            if(keystate[SDLK_ESCAPE])
                goto quit;
        }
// </WIN SCREEN>------------------------------------------------------

// <LOSE SCREEN>------------------------------------------------------
    lose:
        {
            SDL_Surface* losebg = SDL_LoadBMP("youlose.bmp");
            SDL_BlitSurface(losebg,NULL,screen,NULL);
            SDL_Flip(screen);
            while(true)
            {
                //TODO: play cops sfx
                SDL_PumpEvents();
                if(keystate[SDLK_r])
                    break;
                if(keystate[SDLK_ESCAPE])
                    break;
            }
            SDL_FreeSurface(losebg);
            if(keystate[SDLK_r])
                goto title;
            if(keystate[SDLK_ESCAPE])
                goto quit;
        }
// </LOSE SCREEN>------------------------------------------------------

// <CLEANUP AND QUIT>------------------------------------------------------
    quit:
    SDL_Quit();
    return 0;
// </CLEANUP AND QUIT>------------------------------------------------------
}
